/*
 * Created on 03/02/2013 - 3:30 PM
 */
package sembr.vtk.data.input;

import java.util.ArrayList;
import java.util.List;
import vtk.*;

/**
 *
 * @author adriano
 */
public class OPC extends SourceBase 
{  
    private vtkAppendPolyData cylindersCollection;
    private double cylinderHeight;
    private double smallerRadius;
    private double largerRadius;
    private int cylinderResolution;
    private double spaceBetweenLargerRadius;
    private vtkCubeSource inputWaveGuide;
    private double inputWaveGuideWidth;
    private double inputWaveGuideDepth;
    private double inputWaveGuideBounds[];
    private double dieletricOfInputWaveGuide;
    private vtkDoubleArray inputWaveGuideScalars;
    private vtkCubeSource middleWaveGuide;
    private double couplerWidth;
    private double couplerDepth;
    private double couplerHeight;
    private double couplerBounds[];
    private double dieletricOfCoupler;
    private vtkDoubleArray couplerScalars;
    private double[] couplerCenter;
    private int nrOfCylindersColumnsInCoupler;
    private int nrOfCylindersRowsInCoupler;
    private vtkCubeSource outputWaveGuide;
    private double outputWaveGuideWidth;
    private double outputWaveGuideDepth;
    private double outputWaveGuideBounds[];
    private double dieletricOfOutputWaveGuide;
    private vtkDoubleArray outputWaveGuideScalars;
    vtkPolyData photonicDevicePolyData;
           
    public OPC()
    { }
    
    /**
     * @return the cylindersCollection
     */
    public vtkAppendPolyData getCylindersCollection() 
    {
        return cylindersCollection;
    }

    /**
     * @param cylindersCollection the cylindersCollection to set
     */
    public void setCylindersCollection(vtkAppendPolyData cylindersCollection) 
    {
        this.cylindersCollection = cylindersCollection;
    }
    
    /**
     * @return the cylinderHeight
     */
    public double getCylinderHeight() 
    {
        return cylinderHeight;
    }

    /**
     * @param cylinderHeight the cylinderHeight to set
     */
    public void setCylinderHeight(double cylinderHeight) 
    {
        this.cylinderHeight = cylinderHeight + 0.01;
    }
    
    /**
     * @return the smallerRadius
     */
    public double getSmallerRadius() 
    {
        return smallerRadius;
    }

    /**
     * @param smallerRadius the smallerRadius to set
     */
    public void setSmallerRadius(double smallerRadius) 
    {
        this.smallerRadius = smallerRadius;
    }

    /**
     * @return the largerRadius
     */
    public double getLargerRadius() 
    {
        return largerRadius;
    }

    /**
     * @param largerRadius the largerRadius to set
     */
    public void setLargerRadius(double largerRadius) 
    {
        this.largerRadius = largerRadius;
    }

    /**
     * @return the cylinderResolution
     */
    public int getCylinderResolution() 
    {
        return cylinderResolution;
    }

    /**
     * @param cylinderResolution the cylinderResolution to set
     */
    public void setCylinderResolution(int cylinderResolution) 
    {
        this.cylinderResolution = cylinderResolution;
    }
    
    /**
     * @return the inputWaveGuideWidth
     */
    public double getInputWaveGuideWidth() 
    {
        return inputWaveGuideWidth;
    }

    /**
     * @param inputWaveGuideWidth the inputWaveGuideWidth to set
     */
    public void setInputWaveGuideWidth(double inputWaveGuideWidth) 
    {
        this.inputWaveGuideWidth = inputWaveGuideWidth;
    }
    
    /**
     * @return the inputWaveGuideDepth
     */
    public double getInputWaveGuideDepth() 
    {
        return inputWaveGuideDepth;
    }

    /**
     * @param inputWaveGuideDepth the inputWaveGuideDepth to set
     */
    public void setInputWaveGuideDepth(double inputWaveGuideDepth) 
    {
        this.inputWaveGuideDepth = inputWaveGuideDepth;
    }
    
    /**
     * @return the couplerWidth
     */
    public double getCouplerWidth() 
    {
        return couplerWidth;
    }

    /**
     * @return the couplerDepth
     */
    public double getCouplerDepth() 
    {
        return couplerDepth;
    }

    /**
     * @param couplerDepth the couplerDepth to set
     */
    public void setCouplerDepth(double couplerDepth) 
    {
        this.couplerDepth = couplerDepth;
    }
    
    /**
     * @return the outputWaveGuideWidth
     */
    public double getOutputWaveGuideWidth() 
    {
        return outputWaveGuideWidth;
    }

    /**
     * @param couplerWidth the couplerWidth to set
     */
    public void setCouplerWidth(double couplerWidth) 
    {
        this.couplerWidth = couplerWidth;
    }
    
    /**
     * @param outputWaveGuideWidth the outputWaveGuideWidth to set
     */
    public void setOutputWaveGuideWidth(double outputWaveGuideWidth) 
    {
        this.outputWaveGuideWidth = outputWaveGuideWidth;
    }
    
    /**
     * @return the outputWaveGuideDepth
     */
    public double getOutputWaveGuideDepth() 
    {
        return outputWaveGuideDepth;
    }
    
    /**
     * @param outputWaveGuideDepth the outputWaveGuideDepth to set
     */
    public void setOutputWaveGuideDepth(double outputWaveGuideDepth) 
    {
        this.outputWaveGuideDepth = outputWaveGuideDepth;
    }
    
    /**
     * @return the spaceBetweenLargerRadius
     */
    public double getSpaceBetweenLargerRadius() 
    {
        return spaceBetweenLargerRadius;
    }

    /**
     * @param spaceBetweenLargerRadius the spaceBetweenLargerRadius to set
     */
    public void setSpaceBetweenLargerRadius(double spaceBetweenLargerRadius) 
    {
        this.spaceBetweenLargerRadius = spaceBetweenLargerRadius;
    }
    
    /**
     * @return the inputWaveGuideBounds
     */
    public double[] getInputWaveGuideBounds() 
    {
        return inputWaveGuideBounds;
    }

    /** 
     * set input waveguide bound
     * 
     * @param center of graphic object to base the calculate of bounds
     * 
     * Input waveguide bounds is based in its height and width
     */
    public void setInputWaveGuideBounds() 
    {
        double xShift = getInputWaveGuideWidth() / 2;
        double yShift = getCouplerHeight() / 2;
        double zShift = getInputWaveGuideDepth() / 2;
        double inputWaveGuideXCenter = getCouplerBounds()[0] - xShift;        
        
        inputWaveGuideBounds = new double[6];
        // initial and final x axis coordinates
        // left bound
        inputWaveGuideBounds[0] = inputWaveGuideXCenter - xShift;
        // right bound
        inputWaveGuideBounds[1] = inputWaveGuideXCenter + xShift;
    
        // initial and final y axis coordinates
        // bottom bound
        inputWaveGuideBounds[2] = getCouplerCenter()[1] - yShift;
        // top bound
        inputWaveGuideBounds[3] = getCouplerCenter()[1] + yShift;
        
        // initial and final z axis coordinates
        // front bound
        inputWaveGuideBounds[4] = getCouplerCenter()[2] - zShift;
        // back bound
        inputWaveGuideBounds[5] = getCouplerCenter()[2] + zShift;
    }
    
    /**
     * @return the couplerBounds
     */
    public double[] getCouplerBounds() 
    {
        return couplerBounds;
    }

    /**
     * set middleWaveGuide bounds
     * 
     * @param none
     * 
     * Coupler bounds is based on its height and width 
     */
    public void setCouplerBounds() 
    {        
        double xShift = getCouplerWidth() / 2;
        double yShift = getCouplerHeight() / 2;
        double zShift = getCouplerDepth() / 2;
        
        couplerBounds = new double[6];
        // initial and final x axis coordinates
        // left bound
        couplerBounds[0] = getCouplerCenter()[0] - xShift;
        // right bound
        couplerBounds[1] = getCouplerCenter()[0] + xShift;
    
        // initial and final y axis coordinates
        // bottom bound
        couplerBounds[2] = getCouplerCenter()[1] - yShift;
        // top bound
        couplerBounds[3] = getCouplerCenter()[1] + yShift;
        
        // initial and final z axis coordinates
        // front bound
        couplerBounds[4] = getCouplerCenter()[2] - zShift;
        // back bound
        couplerBounds[5] = getCouplerCenter()[2] + zShift;
    }
    
    /**
     * @return the couplerCenter
     */
    public double[] getCouplerCenter() 
    {
        return couplerCenter;
    }

    /**
     * @param couplerCenter the couplerCenter to set
     */
    public void setCouplerCenter(double x, double y, double z) 
    {
        couplerCenter = new double[3];
        couplerCenter[0] = x;
        couplerCenter[1] = y;
        couplerCenter[2] = z;
    }
    
    /**
     * @return the couplerHeight
     */
    public double getCouplerHeight() 
    {
        return couplerHeight;
    }

    /**
     * @param couplerHeight the couplerHeight to set
     */
    public void setCouplerHeight(double couplerHeight) 
    {
        this.couplerHeight = couplerHeight;
    }
    
    /**
     * @return the outputWaveGuideBounds
     */
    public double[] getOutputWaveGuideBounds() 
    {
        return outputWaveGuideBounds;
    }

    /**
     * set output waveguide bounds
     * @param none
     * 
     * Output waveguide bounds is based in its height and width
     */
    public void setOutputWaveGuideBounds() 
    {
        double xShift = getOutputWaveGuideWidth() / 2;
        double outputWaveGuideXCenter = getCouplerBounds()[1] + xShift;
        double yShift = getCouplerHeight() / 2;
        double zShift = getOutputWaveGuideDepth() / 2;
        
        outputWaveGuideBounds = new double[6];
        // initial and final x axis coordinates
        // left bound
        outputWaveGuideBounds[0] = outputWaveGuideXCenter - xShift;
        // right bound
        outputWaveGuideBounds[1] = outputWaveGuideXCenter + xShift;
    
        // initial and final y axis coordinates
        // bottom bound
        outputWaveGuideBounds[2] = getCouplerCenter()[1] - yShift;
        // top bound
        outputWaveGuideBounds[3] = getCouplerCenter()[1] + yShift;
        
        // initial and final z axis coordinates
        // front bound
        outputWaveGuideBounds[4] = getCouplerCenter()[2] - zShift;
        // back bound
        outputWaveGuideBounds[5] = getCouplerCenter()[2] + zShift;
    }    
    
    /**
     * @return the dieletricOfInputWaveGuide
     */
    public double getDieletricOfInputWaveGuide() 
    {
        return dieletricOfInputWaveGuide;
    }

    /**
     * @param dieletricOfInputWaveGuide the dieletricOfInputWaveGuide to set
     */
    public void setDieletricOfInputWaveGuide(double dieletricOfInputWaveGuide) 
    {
        this.dieletricOfInputWaveGuide = dieletricOfInputWaveGuide;
    }

    /**
     * @return the dieletricOfOutputWaveGuide
     */
    public double getDieletricOfOutputWaveGuide() 
    {
        return dieletricOfOutputWaveGuide;
    }

    /**
     * @param dieletricOfOutputWaveGuide the dieletricOfOutputWaveGuide to set
     */
    public void setDieletricOfOutputWaveGuide(double dieletricOfOutputWaveGuide) 
    {
        this.dieletricOfOutputWaveGuide = dieletricOfOutputWaveGuide;
    }

    /**
     * @return the dieletricOfCoupler
     */
    public double getDieletricOfCoupler() 
    {
        return dieletricOfCoupler;
    }

    /**
     * @param dieletricOfCoupler the dieletricOfCoupler to set
     */
    public void setDieletricOfCoupler(double dieletricOfCoupler) 
    {
        this.dieletricOfCoupler = dieletricOfCoupler;
    }
    
    /**
     * @return the inputWaveGuide
     */
    public vtkCubeSource getInputWaveGuide() 
    {
        return inputWaveGuide;
    }

    /**
     * @param inputWaveGuide the inputWaveGuide to set
     */
    public void setInputWaveGuide(vtkCubeSource inputWaveGuide) 
    {
        this.inputWaveGuide = inputWaveGuide;
        this.inputWaveGuide.SetBounds(getInputWaveGuideBounds());
        this.inputWaveGuide.Update();        
    }

    /**
     * @return the middleWaveGuide
     */
    public vtkCubeSource getMiddleWaveGuide() 
    {
        return middleWaveGuide;
    }

    /**
     * @param middleWaveGuide the middleWaveGuide to set
     */
    public void setMiddleWaveGuide(vtkCubeSource middleWaveGuide) 
    {
        this.middleWaveGuide = middleWaveGuide;
        this.middleWaveGuide.SetBounds(getCouplerBounds());
        this.middleWaveGuide.Update();       
    }

    /**
     * @return the outputWaveGuide
     */
    public vtkCubeSource getOutputWaveGuide() 
    {
        return outputWaveGuide;
    }

    /**
     * @param outputWaveGuide the outputWaveGuide to set
     */
    public void setOutputWaveGuide(vtkCubeSource outputWaveGuide) 
    {
        this.outputWaveGuide = outputWaveGuide;
        this.outputWaveGuide.SetBounds(getOutputWaveGuideBounds());
        this.outputWaveGuide.Update();
    }
    
    @Override
    public vtkPolyData getPolyData() 
    {        
        return photonicDevicePolyData;
    }
    
    public void setPolyData(vtkPolyData polyData)
    {
        photonicDevicePolyData = polyData;
    }
            
    public void buildPhotonicDevice()
    { 
        vtkAppendPolyData photonicDevicePd = new vtkAppendPolyData();
        photonicDevicePd.AddInput(buildInputWaveGuide());
        photonicDevicePd.AddInput(buildCoupler());
        photonicDevicePd.AddInput(buildOutputWaveGuide());
        photonicDevicePd.Update();
        
        // set photonic device
        photonicDevicePolyData = refineWaveGuideMesh(photonicDevicePd.GetOutput());
        photonicDevicePolyData.Update();
        
        //vtkDataArray scalarFromCells = photonicDevicePolyData.GetCellData().GetScalars();
        
        //int numberOfTuples = scalarFromCells.GetNumberOfTuples();
        
        //testingScalarValues(photonicDevicePolyData, numberOfTuples);
    }
    
    private void testingScalarValues(vtkPolyData cube, int nrOfCells)
    {
        System.out.println("numero de celulas: " + nrOfCells);
        
        for (int i = 0; i < nrOfCells; i++)
        {
            System.out.println("Dieletrico : " + cube.GetCellData().GetScalars().GetTuple1(i));
        }
    }
    
    private vtkPolyData refineWaveGuideMesh(vtkPolyData pdTobeRefined)
    {
        vtkPolyData triangulatedPd = triangulatePolyData(pdTobeRefined);
        triangulatedPd.Update();
        
        vtkLinearSubdivisionFilter subdivisor = new vtkLinearSubdivisionFilter();
        subdivisor.SetNumberOfSubdivisions(1);
        subdivisor.SetInput(triangulatedPd);
        subdivisor.Update();
        
        return subdivisor.GetOutput();
    }
    
    private vtkPolyData buildInputWaveGuide()
    {
        setInputWaveGuide(new vtkCubeSource());
        
        // set dieletric for input wave guide
        vtkPolyData refinedMesh = refineWaveGuideMesh(getInputWaveGuide().GetOutput());
        refinedMesh.Update();
        int nrOfCellsInInputWaveGuide = refinedMesh.GetNumberOfCells();   
        inputWaveGuideScalars = new vtkDoubleArray();
        inputWaveGuideScalars.SetName("material_1");
        inputWaveGuideScalars.SetNumberOfComponents(1);
        inputWaveGuideScalars.SetNumberOfValues(nrOfCellsInInputWaveGuide);
        
        for (int i = 0; i < nrOfCellsInInputWaveGuide; i++)
        {
            inputWaveGuideScalars.SetValue(i, getDieletricOfInputWaveGuide());
        }
        
        refinedMesh.GetCellData().SetScalars(inputWaveGuideScalars);
        refinedMesh.Update();
        
        return refinedMesh;
    }
    
    /**
     * Coupler is a combination of middle waveguide and cylinders that perfurates it
     * @return perfuratedPolyData
     */
    private vtkPolyData buildCoupler()
    {
        setMiddleWaveGuide(new vtkCubeSource());
        
        vtkPolyData triangulatedMiddleWaveGuide = triangulatePolyData(getMiddleWaveGuide().GetOutput());        
        vtkPolyData triangulatedCylinders = triangulatePolyData(buildCylinders());
        
        // perfurate middle wave guide
        vtkBooleanOperationPolyDataFilter perfuratedPolyData = new vtkBooleanOperationPolyDataFilter();
        perfuratedPolyData.SetOperationToDifference();
        perfuratedPolyData.AddInput(0, triangulatedMiddleWaveGuide);
        perfuratedPolyData.AddInput(1, triangulatedCylinders);
        perfuratedPolyData.Update();
        
        // set dieletric for coupler
        int nrOfCellsInCoupler = perfuratedPolyData.GetOutput().GetNumberOfCells();        
        couplerScalars = new vtkDoubleArray();
        couplerScalars.SetName("material_2");
        couplerScalars.SetNumberOfComponents(1);
        couplerScalars.SetNumberOfValues(nrOfCellsInCoupler);
        
        for (int i = 0; i < nrOfCellsInCoupler; i++)
        {
            couplerScalars.SetValue(i, getDieletricOfCoupler());
        }
        
        perfuratedPolyData.GetOutput().GetCellData().SetScalars(couplerScalars);
        perfuratedPolyData.Update();
        
        return perfuratedPolyData.GetOutput();
    }
    
    private vtkPolyData buildOutputWaveGuide()
    {
        setOutputWaveGuide(new vtkCubeSource());
        
        // set dieletric for output waveguide
        vtkPolyData refinedMesh = refineWaveGuideMesh(getOutputWaveGuide().GetOutput());
        refinedMesh.Update();
        int nrOfCellsInOutputWaveGuide = refinedMesh.GetNumberOfCells();        
        outputWaveGuideScalars = new vtkDoubleArray();
        outputWaveGuideScalars.SetName("material_3");
        outputWaveGuideScalars.SetNumberOfComponents(1);
        outputWaveGuideScalars.SetNumberOfValues(nrOfCellsInOutputWaveGuide);
        
        for (int i = 0; i < nrOfCellsInOutputWaveGuide; i++)
        {
            outputWaveGuideScalars.SetValue(i, getDieletricOfOutputWaveGuide());
        }
        
        refinedMesh.GetCellData().SetScalars(outputWaveGuideScalars);
        refinedMesh.Update();
        
        return refinedMesh;
    }
    
    private vtkPolyData buildCubes()
    {
        setInputWaveGuide(new vtkCubeSource());
        setMiddleWaveGuide(new vtkCubeSource());
        setOutputWaveGuide(new vtkCubeSource());
        
        // set dieletric for input wave guide
        int nrOfCellsInInputWaveGuide = getInputWaveGuide().GetOutput().GetNumberOfCells();        
        inputWaveGuideScalars = new vtkDoubleArray();
        inputWaveGuideScalars.SetNumberOfComponents(1);
        inputWaveGuideScalars.SetNumberOfValues(nrOfCellsInInputWaveGuide);
        
        for (int i = 0; i < nrOfCellsInInputWaveGuide; i++)
        {
            inputWaveGuideScalars.SetValue(i, getDieletricOfInputWaveGuide());
        }
        
        getInputWaveGuide().GetOutput().GetCellData().SetScalars(inputWaveGuideScalars);
        getInputWaveGuide().Update();
               
        // set dieletric for middleWaveGuide
        int nrOfCellsInCoupler = getMiddleWaveGuide().GetOutput().GetNumberOfCells();        
        couplerScalars = new vtkDoubleArray();
        couplerScalars.SetNumberOfComponents(1);
        couplerScalars.SetNumberOfValues(nrOfCellsInCoupler);
        
        for (int i = 0; i < nrOfCellsInCoupler; i++)
        {
            couplerScalars.SetValue(i, getDieletricOfCoupler());
        }
        
        getMiddleWaveGuide().GetOutput().GetCellData().SetScalars(couplerScalars);
        getMiddleWaveGuide().Update();
                
        // set dieletric for output waveguide
        int nrOfCellsInOutputWaveGuide = getOutputWaveGuide().GetOutput().GetNumberOfCells();        
        outputWaveGuideScalars = new vtkDoubleArray();
        outputWaveGuideScalars.SetNumberOfComponents(1);
        outputWaveGuideScalars.SetNumberOfValues(nrOfCellsInOutputWaveGuide);
        
        for (int i = 0; i < nrOfCellsInOutputWaveGuide; i++)
        {
            outputWaveGuideScalars.SetValue(i, getDieletricOfOutputWaveGuide());
        }
        
        getOutputWaveGuide().GetOutput().GetCellData().SetScalars(outputWaveGuideScalars);
        getOutputWaveGuide().Update();
        
        vtkAppendPolyData cubeAppender = new vtkAppendPolyData();
        cubeAppender.AddInput(getInputWaveGuide().GetOutput());
        cubeAppender.AddInput(getMiddleWaveGuide().GetOutput());
        cubeAppender.AddInput(getOutputWaveGuide().GetOutput());
        cubeAppender.Update();
    
        return cubeAppender.GetOutput();
    }
    
    private vtkPolyData buildCylinders()
    {
        setCylindersCollection(new vtkAppendPolyData());
         
        List<double[]> cylinderCenters = buildCylinderCenters();
        double[][] matrixOfCylindersRadius = getMatrixOfCylindersRadius();
        int index = 0;
        vtkCylinderSource currentCylinder;
        int counter = 0;
                
        for (int i = 0; i < (nrOfCylindersRowsInCoupler - 1); i++)
        {                       
            for (int j = 0; j < nrOfCylindersColumnsInCoupler; j++)
            {
                if (matrixOfCylindersRadius[i][j] != 0.00)
                {                   
                    index = i * matrixOfCylindersRadius[i].length + j;
                    System.out.println(cylinderCenters.get(index)[0] + " " +
                                       cylinderCenters.get(index)[1] + " " +
                                       cylinderCenters.get(index)[2] + " ");
                    
                    index = index * 2;
                    // top cylinders
                    currentCylinder = buildCurrentCylinder(cylinderCenters.get(index), matrixOfCylindersRadius[i][j]);
                    currentCylinder.Update();                   
                    getCylindersCollection().AddInput(currentCylinder.GetOutput());
                    getCylindersCollection().Update();
                    // bottom cylinders
                    
                    index++;
                    currentCylinder = buildCurrentCylinder(cylinderCenters.get(index), matrixOfCylindersRadius[i][j]);
                    currentCylinder.Update();                   
                    getCylindersCollection().AddInput(currentCylinder.GetOutput());
                    getCylindersCollection().Update();
                    
                    counter++;
                }               
            }
        }
        
        // build cylinders in center of depth
        for (int j = 0; j < nrOfCylindersColumnsInCoupler; j++)
        {
            index++;
            
            if (matrixOfCylindersRadius[nrOfCylindersRowsInCoupler - 1][j] != 0)
            {
                System.out.println(cylinderCenters.get(index)[0] + " " +
                                   cylinderCenters.get(index)[1] + " " +
                                   cylinderCenters.get(index)[2] + " ");
                    
                // top cylinders
                currentCylinder = buildCurrentCylinder(cylinderCenters.get(index), matrixOfCylindersRadius[nrOfCylindersRowsInCoupler - 1][j]);
                currentCylinder.Update();                   
                getCylindersCollection().AddInput(currentCylinder.GetOutput());
                getCylindersCollection().Update();
            }
        }
        
        System.out.println("counter: " + counter + " - index max: " + index);
        
        // set dieletric for output waveguide
        int nrOfCellsInCylinders = getCylindersCollection().GetOutput().GetNumberOfCells();        
        vtkDoubleArray cylindersScalars = new vtkDoubleArray();
        cylindersScalars.SetNumberOfComponents(1);
        cylindersScalars.SetNumberOfValues(nrOfCellsInCylinders);
        
        for (int i = 0; i < nrOfCellsInCylinders; i++)
        {
            // cylinders' dieletric is the same of middleWaveGuide's dieletric
            cylindersScalars.SetValue(i, getDieletricOfCoupler());
        }
        
        getCylindersCollection().GetOutput().GetCellData().SetScalars(cylindersScalars);
        getCylindersCollection().Update();
        
        return getCylindersCollection().GetOutput();
    }
    
    private vtkCylinderSource buildCurrentCylinder(double[] center, double radius)
    {
        vtkCylinderSource cylinderSource = new vtkCylinderSource();
        cylinderSource.SetCenter(center[0], center[1], center[2]);
        cylinderSource.SetHeight(getCouplerHeight() + 0.01);
        cylinderSource.SetRadius(radius);        
        cylinderSource.SetResolution(getCylinderResolution());
        cylinderSource.CappingOff();
        
        return cylinderSource;
    }
    
    /** 
     * Method to build the centers of cylinders in couple
     * 
     * @return list of cylinderCenters
     * Based in middleWaveGuide's width and height as well cylinder's larger radius.
     * This method is capable to count the number of columns and rows of Coupler,
     * except the middle row of columns won't be count.
     * This method use the simmetry of middleWaveGuide to build top and bottom cylinders
     * 
     */
    private List<double[]> buildCylinderCenters()
    {
        List<double[]> cylinderCenters = new ArrayList<double[]>();
        // cylinder left-top center reference
        double leftTopCenter[] = getLeftTopCenterReference();
        double currentCenter[] = leftTopCenter.clone();
        // cylinder left-bottom center reference
        // reflection of left-top center
        double reflectedCenter[] = new double[3];
        // set reflectedCenter
        reflectedCenter[0] = currentCenter[0];
        reflectedCenter[1] = currentCenter[1];
        // reflection in depth: z axis
        reflectedCenter[2] = currentCenter[2] * (-1);
        
        double spaceBetweenCylinder = getSpaceBetweenLargerRadius() - (2 * getLargerRadius());
        double rightTopXCoordCenter = getCouplerBounds()[1] - spaceBetweenCylinder - getLargerRadius();
        double middleZCoordCenter = getCouplerCenter()[2];
                
        nrOfCylindersColumnsInCoupler = 0;
        nrOfCylindersRowsInCoupler = 0;
        int count = 0;
        
        while (currentCenter[2] > middleZCoordCenter)
        {
            while (currentCenter[0] <= rightTopXCoordCenter)
            {
                System.out.print("[" + currentCenter[0] + ", " + currentCenter[1] + ", " + currentCenter[2] + "] - ");
                System.out.println("[" + reflectedCenter[0] + ", " + reflectedCenter[1] + ", " + reflectedCenter[2] + "]");
                cylinderCenters.add(currentCenter.clone());
                cylinderCenters.add(reflectedCenter.clone());
                // update x coordinates of both centers
                reflectedCenter[0] = currentCenter[0] = currentCenter[0] + getSpaceBetweenLargerRadius();                
                                
                count++;
            }
            
            nrOfCylindersRowsInCoupler += 1;
            // update z coordinates of both centers
            currentCenter[2] = currentCenter[2] - getSpaceBetweenLargerRadius();
            reflectedCenter[2] = currentCenter[2] * (-1);
            // set x coordinates to left
            reflectedCenter[0] = currentCenter[0] = leftTopCenter[0];
        }
        
        nrOfCylindersRowsInCoupler += 1;
        // central cylinders in depth
        // it's important to know that central cylinders will be added at the end of cylinderCenters list
        // the rest of the list alternate the current center and the its respective reflected center
        while (currentCenter[0] <= rightTopXCoordCenter)
        {
            System.out.println("[" + currentCenter[0] + ", " + currentCenter[1] + ", " + currentCenter[2] + "] - ");
            cylinderCenters.add(currentCenter.clone());
            // update x coordinates of both centers
            currentCenter[0] = currentCenter[0] + getSpaceBetweenLargerRadius();                
            nrOfCylindersColumnsInCoupler += 1;        
        }
                
        System.out.println("counter = " + count);
        
        return cylinderCenters;
    }
    
    private double[] getLeftTopCenterReference()
    {
        double spaceBetweenCylinder = getSpaceBetweenLargerRadius() - (2 * getLargerRadius());
        double leftTopXCoordCenter = getCouplerBounds()[0] + (spaceBetweenCylinder + getLargerRadius());
        double leftTopZCoorCenter = getCouplerBounds()[5] - (spaceBetweenCylinder + getLargerRadius());
        
        double referenceCenter[] = new double[3];
        referenceCenter[0] = leftTopXCoordCenter;
        referenceCenter[1] = getCouplerCenter()[1];
        referenceCenter[2] = leftTopZCoorCenter;
        
        return referenceCenter;
    }
    
    /**
     * Hard-Coded Method
     * @return matrix of radius of All cylinders
     * 
     * This method substitutes the means to set the matrix of radius cylinders
     * probably, that matrix set will come from GUI
     * 
     */
    private double[][] getMatrixOfCylindersRadius()
    {
        double presentedCylinders[][] = new double[nrOfCylindersRowsInCoupler][nrOfCylindersColumnsInCoupler];
        
        presentedCylinders[0] = new double[]{0.00, 0.15, 0.00, 0.15, 0.00, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15}; // 3 zeros
        presentedCylinders[1] = new double[]{0.15, 0.15, 0.00, 0.15, 0.00, 0.15, 0.00, 0.15, 0.00, 0.15, 0.00, 0.10}; // 5 zeros
        presentedCylinders[2] = new double[]{0.15, 0.15, 0.15, 0.00, 0.15, 0.00, 0.00, 0.15, 0.15, 0.15, 0.00, 0.15}; // 4 zeros
        presentedCylinders[3] = new double[]{0.15, 0.00, 0.10, 0.15, 0.15, 0.00, 0.15, 0.00, 0.15, 0.00, 0.15, 0.15}; // 4 zeros
        presentedCylinders[4] = new double[]{0.10, 0.15, 0.15, 0.15, 0.00, 0.00, 0.15, 0.10, 0.10, 0.00, 0.00, 0.00}; // 5 zeros
        presentedCylinders[5] = new double[]{0.15, 0.00, 0.15, 0.15, 0.15, 0.00, 0.00, 0.00, 0.15, 0.00, 0.15, 0.15}; // 5 zeros
        presentedCylinders[6] = new double[]{0.00, 0.15, 0.15, 0.00, 0.00, 0.10, 0.15, 0.15, 0.10, 0.15, 0.15, 0.10}; // 3 zeros
        presentedCylinders[7] = new double[]{0.15, 0.00, 0.15, 0.15, 0.15, 0.15, 0.00, 0.00, 0.00, 0.10, 0.10, 0.15}; // 4 zeros
        presentedCylinders[8] = new double[]{0.00, 0.10, 0.10, 0.10, 0.00, 0.10, 0.10, 0.10, 0.10, 0.15, 0.15, 0.00}; // 3 zeros
        
        return presentedCylinders;
    }
      
    public vtkPanel testingVtkBox()
    {          
        double cylinderRadius = 0.15;
        double spaceBetweenCylinders = 0.046153846;
        double shift = cylinderRadius + spaceBetweenCylinders;
        double x = -2.1 + shift;
        double z = -2.97 + shift;
        int counter = 0;
        
        // left cube
        vtkCubeSource leftCube = new vtkCubeSource();
        leftCube.SetBounds(-2.1 - (6 * cylinderRadius), -2.1, -0.1, 0.1, -1.0, 1.0);
        
        // main cube
        vtkCubeSource mainCube = new vtkCubeSource();
        mainCube.SetCenter(0., 0., 0.);
        mainCube.SetBounds(-2.1, 2.1, -0.1, 0.1, -2.97, 2.97);
                      
        vtkCubeSource rightCube = new vtkCubeSource();
        rightCube.SetBounds(2.1, 2.1 + (6 * cylinderRadius), -0.1, 0.1, -0.1, 0.1);
        
        vtkAppendPolyData cubeAppender = new vtkAppendPolyData();
        cubeAppender.AddInput(leftCube.GetOutput());
        cubeAppender.AddInput(mainCube.GetOutput());
        cubeAppender.AddInput(rightCube.GetOutput());
        
        vtkTriangleFilter triangulatedCubeAppender = new vtkTriangleFilter();
        triangulatedCubeAppender.SetInput(cubeAppender.GetOutput());
        triangulatedCubeAppender.Update();
        
        int nrOfPointsInTriangulatedCube = triangulatedCubeAppender.GetOutput().GetPoints().GetNumberOfPoints();
        
        double appendedCubeDieletric = 1.45;
        vtkDoubleArray cubeAppenderScalars = new vtkDoubleArray();
        cubeAppenderScalars.SetNumberOfComponents(1);
        cubeAppenderScalars.SetNumberOfValues(nrOfPointsInTriangulatedCube);
        
        // set scalar value for combined cubes
        for (int i = 0; i < nrOfPointsInTriangulatedCube; i++)
        {
            cubeAppenderScalars.SetValue(i, appendedCubeDieletric);
        }
        
        triangulatedCubeAppender.GetOutput().GetPointData().SetScalars(cubeAppenderScalars);
        triangulatedCubeAppender.Update();
       
        vtkPolyDataMapper cubeAppenderMapper = new vtkPolyDataMapper();
        cubeAppenderMapper.SetInput(triangulatedCubeAppender.GetOutput());
        cubeAppenderMapper.ScalarVisibilityOff();
                
        vtkActor cubeAppenderActor = new vtkActor();
        cubeAppenderActor.SetMapper(cubeAppenderMapper);
        cubeAppenderActor.GetProperty().SetColor(0., 0., 0.);
        
        vtkCylinderSource cylinderSource;
        vtkCylinderSource smallerCylinder;
        vtkTriangleFilter cylinderTriangulator;
        vtkTriangleFilter smallerCylinderTringulator;
        vtkAppendPolyData cylinderSourceAppender = new vtkAppendPolyData();
        vtkAppendPolyData smallerCylinderAppender = new vtkAppendPolyData();
        
        while (x <= (2.1 - shift))
        {           
            while (z <= (2.97 - shift))
            {
                cylinderSource = new vtkCylinderSource();                
                cylinderSource.SetCenter(x, 0., z);
                // cylinder height higher than square side
                // it is to apply correct difference in vtk
                cylinderSource.SetHeight(0.2005);
                cylinderSource.SetRadius(cylinderRadius);
                cylinderSource.CappingOn();
                cylinderSource.SetResolution(30);
                cylinderSource.Update();
                
                smallerCylinder = new vtkCylinderSource();
                smallerCylinder.SetCenter(x, 0., z);
                smallerCylinder.SetHeight(0.201);
                smallerCylinder.SetRadius(0.1);
                smallerCylinder.CappingOn();
                smallerCylinder.SetResolution(30);
                smallerCylinder.Update();
                
                cylinderTriangulator = new vtkTriangleFilter();
                cylinderTriangulator.SetInputConnection(cylinderSource.GetOutputPort());
                cylinderTriangulator.Update();
                
                smallerCylinderTringulator = new vtkTriangleFilter();
                smallerCylinderTringulator.SetInputConnection(smallerCylinder.GetOutputPort());
                smallerCylinderTringulator.Update();
                                
                cylinderSourceAppender.AddInput(cylinderTriangulator.GetOutput());
                smallerCylinderAppender.AddInput(smallerCylinderTringulator.GetOutput());
                cylinderSourceAppender.Update();
                smallerCylinderAppender.Update();
                
                System.out.print("x = " + x + " - z = " + z + " || ");
                
                // update z axis coordenates' value
                z = z + (2 * cylinderRadius + spaceBetweenCylinders);                
                counter++;
            }
               
            System.out.println();
            // original z value
            z = -2.97 + shift;
            // update z axis coordenates' value
            x = x + (2 * cylinderRadius + spaceBetweenCylinders);
        } 
        
        System.out.println("number of holes: " + counter);
                
        int nrPointsInCylinderSource = cylinderSourceAppender.GetOutput().GetPoints().GetNumberOfPoints();
        double cylinderDieletric = 1.98;
        vtkDoubleArray cylindersScalars = new vtkDoubleArray();
        cylindersScalars.SetNumberOfComponents(1);
        cylindersScalars.SetNumberOfValues(nrPointsInCylinderSource);
        
        for (int i = 0; i < nrPointsInCylinderSource; i++)
        {
            cylindersScalars.SetValue(i, cylinderDieletric);
        }
        
        int nrPointsInSmallerCylinder = smallerCylinderAppender.GetOutput().GetPoints().GetNumberOfPoints();
        double smallerCylinderDieletric = 3.5;
        vtkDoubleArray smallerCylindersScalars = new vtkDoubleArray();
        smallerCylindersScalars.SetNumberOfComponents(1);
        smallerCylindersScalars.SetNumberOfValues(nrPointsInSmallerCylinder);
        
        for (int i = 0; i < nrPointsInSmallerCylinder; i++)
        {
            smallerCylindersScalars.SetValue(i, smallerCylinderDieletric);
        }
               
        // vtkTransform transformer = new vtkTransform();
        // transformer.PostMultiply();
        // transformer.RotateX(90.);
        // transformer.Update();
        
        // vtkTransformPolyDataFilter cylinderSourceTransformFilter = new vtkTransformPolyDataFilter();
        // cylinderSourceTransformFilter.SetInput(cylinderSourceAppender.GetOutput());
        // cylinderSourceTransformFilter.SetTransform(transformer);    
        // cylinderSourceTransformFilter.Update();
        
        // vtkTransformPolyDataFilter smallerCylinderTransformFilter = new vtkTransformPolyDataFilter();
        // smallerCylinderTransformFilter.SetInput(smallerCylinderAppender.GetOutput());
        // smallerCylinderTransformFilter.SetTransform(transformer);    
        // smallerCylinderTransformFilter.Update();
                                            
        // vtkBooleanOperationPolyDataFilter booleanOperator = new vtkBooleanOperationPolyDataFilter();
        // booleanOperator.SetOperationToUnion();
        // booleanOperator.SetInputConnection(0, triangulatedCubeAppender.GetOutputPort());
        // booleanOperator.SetInputConnection(1, cylinderSourceTransformFilter.GetOutputPort());
        
        // vtkBooleanOperationPolyDataFilter booleanOperator2 = new vtkBooleanOperationPolyDataFilter();
        // booleanOperator2.SetOperationToUnion();
        // booleanOperator2.SetInputConnection(0, cylinderSourceTransformFilter.GetOutputPort());
        // booleanOperator2.SetInputConnection(1, booleanOperator.GetOutputPort());
        // booleanOperator2.Update();
        
        // vtkPolyDataMapper booleanMapper = new vtkPolyDataMapper();
        // booleanMapper.SetInputConnection(booleanOperator.GetOutputPort());
        // booleanMapper.ScalarVisibilityOn();
        
        vtkPolyDataMapper cylinderAppenderMapper = new vtkPolyDataMapper();
        cylinderAppenderMapper.SetInput(cylinderSourceAppender.GetOutput());
        cylinderAppenderMapper.ScalarVisibilityOff();
        
        vtkPolyDataMapper smallerCylinderAppenderMapper = new vtkPolyDataMapper();
        smallerCylinderAppenderMapper.SetInput(smallerCylinderAppender.GetOutput());
        smallerCylinderAppenderMapper.ScalarVisibilityOff();        
        
        // vtkActor booleanActor = new vtkActor();
        // booleanActor.SetMapper(booleanMapper);
        // booleanActor.GetProperty().SetColor(0., 0., 0.);
        // booleanActor.GetProperty().SetOpacity(0.1);
        
        vtkActor cylinderAppenderActor = new vtkActor();
        cylinderAppenderActor.SetMapper(cylinderAppenderMapper);
        cylinderAppenderActor.GetProperty().SetColor(0.3, 0.5, 0.5);
        
        vtkActor smallerCylinderAppenderActor = new vtkActor();
        smallerCylinderAppenderActor.SetMapper(smallerCylinderAppenderMapper);
        smallerCylinderAppenderActor.GetProperty().SetColor(1. , 0., 0.);
        
        vtkPanel panel = new vtkPanel();
        panel.GetRenderer().AddActor(smallerCylinderAppenderActor);
        panel.GetRenderer().AddActor(cylinderAppenderActor);
        panel.GetRenderer().AddActor(cubeAppenderActor);
        panel.GetRenderer().ResetCamera();
        panel.GetRenderer().SetBackground(1.0, 1.0, 1.0);
        
        return panel;
    }   
}
